// 泛型 与 trait

use std::fmt::Display;
use std::ops::Add;

// 结构中泛型
#[derive(Debug)]
struct Point<T> {
    x: T,
    y: T,
}

// 方法泛型
impl<T> Point<T> {
    fn x(&self) -> &T { // 不能直接 -> T，返回类型T可能引用
        &self.x // 不能 self.x
    }

}

 // 限定 T 必须实现Add，否则不能进行 + 操作
impl<T: Add<T, Output = T>> Add for Point<T> { 
    type Output = Point<T>; // 实现 Add ，必须加这一行
    fn add(self, point: Point<T>) -> Point<T> { // 产生新的Point
        Point { x: self.x + point.x, y: self.y + point.y }
    } 
}

// const 泛型
fn display_array<T: std::fmt::Debug, const N: usize> (arr: [T; N]) {
    println!("array: {:?}", arr);
}

pub trait Summary {
    fn summarize(&self) -> String {
        format!("请自己实现本方法") // 可以有默认实现
    }
}

// 文章
struct Post {
    title: String,
    author: String,
}

impl Summary for Post {
    fn summarize(&self) -> String {
        format!("文章标题：{}, 作者：{}", self.title, self.author)
    }
}

// 微博
struct Weibo {
    username: String,
    content: String,
}

impl Summary for Weibo {
    fn summarize(&self) -> String {
        format!("用户：{}， 微博内容：{}", self.username, self.content)
    }
}

// Trait 作为函数参数
// 本质是：pub fn notify<T: Summary>(item: &T)，即特征约束
pub fn notify(item: &impl Summary) { 
    println!("Trait作为参数：{}", item.summarize());
}

// 有条件的约束
struct Pair<T> {
    x: T,
    y: T,
}

impl<T> Pair<T> {
    fn new(x:T, y: T) -> Self {
        Self { x, y }
    }
}
// 同时实现了Display + PartialOrd 的 Pair<T> 才可以拥有此方法。
impl <T: Display + PartialOrd> Pair<T> {
    fn display(&self) {
        if self.x > self.y {
            println!("x大：{}", self.x);
        } else {
            println!("y大：{}", self.y);
        }
    }
}

#[derive(Debug, PartialEq)]
enum FileState{
    Open,
    Closed,
}

impl Display for FileState {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match *self {
            FileState::Closed => write!(f, "OPEN"),
            FileState::Open => write!(f, "CLOSED"),
        }
    }
}

#[derive(Debug)]
struct File {
    name: String,
    data: Vec<u8>,
    state: FileState,
}

impl Display for File {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "<{} ({})>", self.name, self.state) // 对应成员也必须实现了Display（或默认就有Dispaly）
    }
}

impl File {
    fn new(name: String) -> File {
        File {
            name,
            data: Vec::new(),
            state: FileState::Closed,
        }
    }
}


pub trait Draw {
    fn draw(&self);
}

#[derive(Debug)]
struct Button {
    width: u32,
    height: u32,
    label: String,
}

impl Draw for Button {
    fn draw(&self) {
        println!("{:?}", self);
    }
}

#[derive(Debug)]
struct SelectBox {
    width: u32,
    height: u32,
    options: Vec<String>,
}

impl Draw for SelectBox {
    fn draw(&self) {
        println!("{:?}", self);
    }
}

struct Screen {
    components: Vec<Box<dyn Draw>>,
}
impl  Screen {
    fn run(&self) {
        for item in self.components.iter() {
            item.draw();
        }
    }
}

fn main() {

    println!("add<i32>: {}, add<f64>: {}", add(12, 13), add(3.2, 2.3));

    let p1 = Point{x: 123, y: 321};
    let p2 = Point{x: 1.2, y: 2.3};
    println!("p1.x = {}, p1:{:?}, p2: {:?}", p1.x(),  p1, p2);
    let p3 = Point{x: 2.1, y: 3.2};
    println!("p2 + p3 = {:?}", p2 + p3);

    let arr1: [i32; 3] = [21, 32, 34];
    let arr2: [f64; 2] = [3.2, 2.4];
    display_array(arr1);
    display_array(arr2);
    println!("{:?}", arr1);

    let post = Post {
        title: "Trait 学习".to_string(),
        author: "LIN XQ".to_string(),
    };
    println!("Post: {}", post.summarize());
    let weibo = Weibo {
        username: "Melody".to_string(),
        content: "微博不好用".to_string(),
    };
    println!("Weibo: {}", weibo.summarize());

    notify(&post);

    let pair = Pair::new(32, 12);
    pair.display();


    // cumstom Display
    let f = File::new("rust.txt".to_string());
    println!("{:?}", f); // Debug输出    
    println!("{}", f); // 自定义的输出

    let screen = Screen {
        components: vec![
            Box::new(Button{
                width: 50,
                height: 10,
                label: String::from("OK"),
            }),
            Box::new(SelectBox{
                width: 75,
                height: 10,
                options: vec![
                String::from("Yes"),
                String::from("Maybe"),
               String::from("No")
                ],
            }),
        ],
    };
    screen.run();
    
}

// 需要对 T 进行限制，不是所有类型都可以相加
fn add<T: std::ops::Add<Output = T>>(x: T, y: T) -> T {
    x + y
}